#include "serialport.h"
#include <stdio.h>
#include <termios.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <time.h>
#include <cstdlib>
#include <cmath>
#include "jni.h"
#include "hex.h"

static bool flag_stopOtherThreadRead = false;

// int main(int argc, char const *argv[])
// {
// 	int fd = open_serialport("dev/ttyS1",115200,0);
// 	char ch[] = "dev/ttyS1";
// 	write_serialport(fd,ch,9);
// 	settimeout(fd,5.0);
// 	int len = 130000;
// 	char pbuf[len];
// 	int ret = read(fd,pbuf,len);
// 	printf("ret : %d\n", ret);


// 	return 0;
// }


static speed_t getBaudrate(int baudrate) {
    switch (baudrate) {
        case 0:
            return B0;
        case 50:
            return B50;
        case 75:
            return B75;
        case 110:
            return B110;
        case 134:
            return B134;
        case 150:
            return B150;
        case 200:
            return B200;
        case 300:
            return B300;
        case 600:
            return B600;
        case 1200:
            return B1200;
        case 1800:
            return B1800;
        case 2400:
            return B2400;
        case 4800:
            return B4800;
        case 9600:
            return B9600;
        case 19200:
            return B19200;
        case 38400:
            return B38400;
        case 57600:
            return B57600;
        case 115200:
            return B115200;
        case 230400:
            return B230400;
        case 460800:
            return B460800;
        case 500000:
            return B500000;
        case 576000:
            return B576000;
        case 921600:
            return B921600;
        case 1000000:
            return B1000000;
        case 1152000:
            return B1152000;
        case 1500000:
            return B1500000;
        case 2000000:
            return B2000000;
        case 2500000:
            return B2500000;
        case 3000000:
            return B3000000;
        case 3500000:
            return B3500000;
        case 4000000:
            return B4000000;
        default:
            return -1;
    }
}

int open_serialport(const char *path, int baudrate, int flags) {

    int fd;
    speed_t speed;

    int err;
    struct termios curr_term;
    //LOGI("------------------Java_android_serialport_SerialPort_open--------");
    /* Check arguments */
    {
        speed = getBaudrate(baudrate);
        if (speed == -1) {
            /* TODO: throw an exception */
            //LOGE("Invalid baudrate");
            return -1;
        }
    }

    /* Opening device */
    {

        fd = open(path, O_RDWR | O_NOCTTY | O_NONBLOCK | O_NDELAY);
        //LOGD("open() fd = %d", fd);
        if (fd == -1) {
            /* Throw an exception */
            //LOGE("Cannot open port");
            /* TODO: throw an exception */
            return -1;
        }
    }

    /* Configure device */
    {

        if ((err = tcgetattr(fd, &curr_term)) != 0) {
            //printf("gsd4e_channel_open: tcgetattr(%d) = %d,  errno %d\r\n", fd, err, errno);
            close(fd);
            return -1;
        }

        cfmakeraw(&curr_term);

        tcsetattr(fd, TCSANOW, &curr_term);


        tcflush(fd, TCIOFLUSH);


        if (cfsetispeed(&curr_term, speed)) {
            close(fd);
            return -1;
        }

        if (cfsetospeed(&curr_term, speed)) {
            close(fd);
            return -1;
        }
        tcsetattr(fd, TCSANOW, &curr_term);
    }

    return fd;

//    int fd;
//    speed_t speed;
//
//    int err;
//    struct termios curr_term;
//        speed = getBaudrate(baudrate);
//        if (speed == -1) {
//            return -1;
//        }
//
//
//
//        fd = open(path, O_RDWR | O_NOCTTY | O_NONBLOCK | O_NDELAY);
//
//        if (fd == -1) {
//            return -1;
//        }
//
//
//
//        tcflush(fd, TCIOFLUSH);
//
//        if ((err = tcgetattr(fd, &curr_term)) != 0) {
//            close(fd);
//            return -1;
//        }
//        curr_term.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR
//                               | ICRNL | IXON);
//        curr_term.c_oflag &= ~OPOST;
//        curr_term.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
//        curr_term.c_cflag &= ~(CSIZE | PARENB);
//        curr_term.c_cflag |= CS8;
//        curr_term.c_cflag &= ~CRTSCTS;
//
//    curr_term.c_cc[VTIME] = 0;
//    curr_term.c_cc[VMIN] = 0;
//
//
//        tcsetattr(fd, TCSANOW, &curr_term);
//
//
//        tcflush(fd, TCIOFLUSH);
//
//        if (cfsetispeed(&curr_term, speed)) {
//            close(fd);
//            return -1;
//        }
//
//        if (cfsetospeed(&curr_term, speed)) {
//            close(fd);
//            return -1;
//        }
//        tcsetattr(fd, TCSANOW, &curr_term);
//    return fd;

//    int fd;
//    speed_t speed;
//
//    int err;
//    struct termios curr_term;
//
//    speed = getBaudrate(baudrate);
//    fd = open(path, O_RDWR );
//    if (fd < 0) {
//        return -1;
//    }
//
//    tcgetattr(fd, &curr_term);
//    cfmakeraw(&curr_term);
//    cfsetispeed(&curr_term, speed);
//    cfsetospeed(&curr_term, speed);
//    tcflush(fd, TCIFLUSH);
//
//    if(tcsetattr(fd,TCSANOW,&curr_term)!=0)
//        return -1; //设置串口失败
//
//    return fd;

}


unsigned long GetTickCount() {
    struct timespec ts;

    clock_gettime(CLOCK_MONOTONIC, &ts);

    return (ts.tv_sec * 1000 + ts.tv_nsec / 1000000);
}

void write_serialport(int fd, const uint8_t *cmd, int len) {
    write(fd, cmd, len);
}

void stop_read() {
    flag_stopOtherThreadRead = true;
    for (int i = 0; i < 3; i++)
        usleep(1000 * 15);
}

/*
 * 如果是在任务中，需要不停地写入数据到串口设备，千万不能在每次写入数据到设备前，
 * 进行flush以前数据的操作，因为两次写入的间隔是业务控制的，内核不会保证在两次写入
 * 之间一定把数据发送成功。flush操作一般在打开或者复位串口设备时进行操作。
 * */
void flush(int fd) {
    tcflush(fd, TCIOFLUSH);
    usleep(10 * 1000);
}


int read_serialport(int fd, uint8_t *pbuf, int len, int timems) {
    unsigned long start;
    int left, n, i;
    start = GetTickCount();
    left = len;
    flag_stopOtherThreadRead = false;
    while (left > 0 && !flag_stopOtherThreadRead) {
        n = read(fd, (char *) pbuf, left);
        if (n > 0) {
            left -= n;
            pbuf += n;
        }
        if (fabs(GetTickCount() - start) > timems) {
            break;
        }
        usleep(1000 * 10);
    }
    return len - left;
}


void close_serialport(int fd) {
    close(fd);
}

extern "C"
JNIEXPORT void JNICALL
Java_cd_serialport_Serialport_flush(JNIEnv *env, jobject instance, jint fd) {
    flush(fd);

}extern "C"
JNIEXPORT jint JNICALL
Java_cd_serialport_Serialport_open_1serialport(JNIEnv *env, jobject instance, jstring path_,
                                               jint baudrate, jint flags) {
    int ret = -1;
    const char *path = env->GetStringUTFChars(path_, 0);
    ret = open_serialport(path, baudrate, flags);

    env->ReleaseStringUTFChars(path_, path);
    return ret;
}extern "C"
JNIEXPORT void JNICALL
Java_cd_serialport_Serialport_close_1serialport(JNIEnv *env, jobject instance, jint fd) {
    close_serialport(fd);
}extern "C"
JNIEXPORT jint JNICALL
Java_cd_serialport_Serialport_read_1serialport(JNIEnv *env, jobject instance, jint fd,
                                               jbyteArray pbuf_, jint len, jint timems) {
    int ret = -1;
    jbyte *pbuf = env->GetByteArrayElements(pbuf_, NULL);
    ret = read_serialport(fd, (uint8_t *) pbuf, len, timems);
    env->ReleaseByteArrayElements(pbuf_, pbuf, 0);
    return ret;
}extern "C"
JNIEXPORT void JNICALL
Java_cd_serialport_Serialport_write_1serialport(JNIEnv *env, jobject instance, jint fd,
                                                jbyteArray cmd_, jint len) {
    jbyte *cmd = env->GetByteArrayElements(cmd_, NULL);
    write_serialport(fd, (const uint8_t *) cmd, len);
    env->ReleaseByteArrayElements(cmd_, cmd, 0);
}